#include "share_mem.h"

static int isServer;
static shmemT shmem;

static int sendDataServer(void * data, int size);
static int sendDataClient(void * data, int size);
static int recvDataServer(void * data);
static int recvDataClient(void * data);

static void init_client();
static void init_server();

static void init_mem(void);
static void init_sem(void);

static void close_server(void);
static void close_client(void);

/* DEBUG */
static void printStatus(void);


void 
init(int isThisTheServer )
{
 	isServer = isThisTheServer;
	if(isServer)
	{
		atexit(close_server);
		init_server();
	}
}

int 
sendData(void * data, int size)
{
	if(isServer)
		return sendDataServer(data, size);
	else
		return sendDataClient(data, size);

}

int
recvData(void * data)
{
	if(isServer)
		return recvDataServer(data);
	else
		return recvDataClient(data);
}

/* 
**********************
** Auxiliar functions
**********************
*/

static int
sendDataServer(void * data, int size)
{
	struct databuf * buf;
	struct sembuf v3={2,1,0};
	
	buf=shmem.buf;

	memcpy(buf->d_buf, data, size);
	buf->d_nread=size;
	/* enable the answer */
	semop(shmem.semid, &v3, 1);
	
	return size;
}

static int
sendDataClient(void * data, int size)
{
	struct databuf * buf;
	struct sembuf p1 ={0,-1,0};
	struct sembuf v2={1,1,0};
	
	init_client();
	buf=shmem.buf;
	/* wait the habilitation */
	semop(shmem.semid, &p1, 1);
	memcpy(buf->d_buf, data, size);
	buf->d_nread=size;
		
	/* signals end of writings */
	semop(shmem.semid, &v2, 1);

	close_client();
	
	return size;
}

static int
recvDataServer(void * data)
{
	struct databuf * buf;
	struct sembuf p2={1,-1,0};
	
	buf=shmem.buf;
	/* wait for a request  */
	semop(shmem.semid, &p2, 1);
	memcpy(data, buf->d_buf, buf->d_nread);
	return buf->d_nread;

}

static int
recvDataClient(void * data)
{
	struct databuf * buf;
	struct sembuf p3 ={2,-1,0};
	struct sembuf v1 ={0,1,0};
	double * resp;
	int size;
	
	init_client();
	buf=shmem.buf;
	/* wait the server answer */
	semop(shmem.semid, &p3, 1);
	memcpy(data, buf->d_buf, buf->d_nread);
	resp= (double *)data;
	size=buf->d_nread;
	
	/* enable a new request */
	semop(shmem.semid, &v1, 1);
	
	close_client();

	return size;
}


/* make client init configuration */
static void
init_client()
{	
	/* join to a existing share mem (parameter zero) */
	if((shmem.shmid=shmget(SHMKEY, sizeof(struct databuf), 0))==-1)
		fatal("Client: Error while opening the shared memory");/* the server is down */

	/* attach shared memory segment */
	if((shmem.buf=shmat(shmem.shmid, 0,0))==ERR)
		fatal("Client: Error while attaching the shared memory");

	/* join to a existing semaphore */
	if((shmem.semid=semget(SEMKEY, 3, 0))==-1)
		fatal("Client: Error while joing the semaphore set");

}


static void
init_server()
{
	init_mem();
	init_sem();

}

static void
init_mem(void)
{

	/* create shared memory segment */
	if((shmem.shmid=shmget(SHMKEY, sizeof(struct databuf), 0666 | IPC_CREAT))==-1)
			fatal("Server: Error while creating the shared memory");

	/* attach shared memory segment */
	if((shmem.buf=shmat(shmem.shmid, 0,0))==ERR)
		fatal("Server: Error while attaching shared memory");

}


static void
init_sem(void)
{
	semun x;

	/* create three semaphore set */
	if((shmem.semid=semget(SEMKEY, 3, 0666 | IPC_CREAT))==-1)
		fatal("Server: Error while creating the semaphore set");

	/* set initial values */
	x.val=1;
	if(semctl(shmem.semid, 0, SETVAL, x)==-1)
		fatal("Server: Error while initialization semaphore");

	x.val=0;
	if(semctl(shmem.semid, 1, SETVAL, x)==-1)
		fatal("Server: Error while initialization semaphore");

	if(semctl(shmem.semid, 2, SETVAL, x)==-1)
		fatal("Server: Error while initialization semaphore");

}

static void
close_server(void)
{
	/* detach shared memory */
	if(shmdt(shmem.buf)==-1)
		fatal("Server: Error while dettaching the shared memory");
		
	/* close the shared memory */
	if(shmctl(shmem.shmid, IPC_RMID, NULL)==-1)
		fatal("Server: Error while removing the shared memory");
	
	if(semctl(shmem.semid, 0,IPC_RMID)==-1)
		fatal("Server: Error while removing semaphore");
	
}

static void
close_client(void)
{
	/* detach shared memory */
	if(shmdt(shmem.buf)==-1)
		fatal("Client: Error while dettaching the shared memory");
}

/* DEBUG */
static void
printStatus()
{
	int i, c;
	for(i=0; i<3; i++)
	{
		c=semctl(shmem.semid, i, GETVAL, 0);
		printf("valor de %d es: %d\n", i, c);
	}

}

